/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file crc_drv.c
 * @brief This file defines crc driver functions
 */

/*******Includes***************************************************************/
#include <stddef.h>
#include "crc_reg_access.h"
#include "clock_drv.h"

/*******Definitions************************************************************/

/*******Variables**************************************************************/
/*! @brief Table to save register base addresses for CRC instances */
static CRC_Type* const s_crcRegBase[CONFIG_CRC_INSTANCE_COUNT] = CONFIG_CRC_REG_BASE_PTRS;

/*! @brief Table to save module names for CRC instances */
static const module_names_t s_crcModuleName[CONFIG_CRC_INSTANCE_COUNT] = CONFIG_CRC_MODULE_NAMES;

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : CRC_Init
 *** Description   : Initializes the CRC module
 ******************************************************************************/
void CRC_Init(uint32_t instance, const crc_config_t* crcConfig)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);
    ASSERT_PARAM(crcConfig != NULL);

    CRC_Type* regBase = s_crcRegBase[instance];

    /* Enable clock of the CRC module */
    CLOCK_SetModuleBusClockEnable(s_crcModuleName[instance], true);
    /* Set soft reset for CRC module to inactive */
    CLOCK_SetModuleSoftResetState(s_crcModuleName[instance], RESET_INACTIVE);

    /* Width of CRC protocol */
    CRC_REG_SetProtocol(regBase, crcConfig->crcProtocol);
    /* Set Type Of Transpose for Write */
    CRC_REG_SetWriteTranspose(regBase, crcConfig->writeTranspose);
    /* Set Type Of Transpose for Read */
    CRC_REG_SetReadTranspose(regBase, crcConfig->readTranspose);
    /* Complement Read Of CRC Data */
    CRC_REG_SetChecksumXor(regBase, crcConfig->checksumXOR);
    /* Set CRC polynomial */
    CRC_REG_SetPoly(regBase, crcConfig->polynomial);

    /* Write a seed - initial checksum */
    if (crcConfig->crcProtocol == CRC16_PROTOCOL) {
        CRC_REG_SetSeedL(regBase, (uint16_t)crcConfig->seed);
    } else {
        CRC_REG_SetSeed(regBase, crcConfig->seed);
    }
}

/*******************************************************************************
 *** Function Name : CRC_Deinit
 *** Description   : DeInitializes the CRC module
 ******************************************************************************/
void CRC_Deinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);

    /* Set soft reset for CRC module to active */
    CLOCK_SetModuleSoftResetState(s_crcModuleName[instance], RESET_ACTIVE);
    /* Disable clock of the CRC module */
    CLOCK_SetModuleBusClockEnable(s_crcModuleName[instance], false);
}

/*******************************************************************************
 *** Function Name : CRC_CalcCrc32InByte
 *** Description   : Start CRC32 calculate for a block of bytes
 ******************************************************************************/
uint32_t CRC_CalcCrc32InByte(uint32_t instance,
                             const uint8_t* dataBuff,
                             uint32_t dataLength,
                             uint32_t seed,
                             bool isNewSeed)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);
    ASSERT_PARAM(dataBuff != NULL);

    uint32_t retValue;
    CRC_Type* regBase = s_crcRegBase[instance];

    /* True if the isNewSeed is, then nedd to set the seed */
    if (isNewSeed) {
        CRC_REG_SetSeed(regBase, seed);
    }

    /* Write the data that from the dataBuff to DATA register to calculate checksum */
    for (uint32_t i = 0; i < dataLength; i++) {
        CRC_REG_SetDataLL(regBase, dataBuff[i]);
    }

    /* Get the result by read DATA register */
    retValue = CRC_REG_GetData(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : CRC_CalcCrc32InHalfword
 *** Description   : Start CRC32 calculate for a block of Halfwords
 ******************************************************************************/
uint32_t CRC_CalcCrc32InHalfword(uint32_t instance,
                                 const uint16_t* dataBuff,
                                 uint32_t dataLength,
                                 uint32_t seed,
                                 bool isNewSeed)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);
    ASSERT_PARAM(dataBuff != NULL);

    uint32_t retValue;
    CRC_Type* regBase = s_crcRegBase[instance];

    /* True if the isNewSeed is, then nedd to set the seed */
    if (isNewSeed) {
        CRC_REG_SetSeed(regBase, seed);
    }

    /* Write the data that from the dataBuff to DATA register to calculate checksum */
    for (uint32_t i = 0; i < dataLength; i++) {
        CRC_REG_SetDataL(regBase, dataBuff[i]);
    }

    /* Get the result by read DATA register */
    retValue = CRC_REG_GetData(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : CRC_CalcCrc32InWord
 *** Description   : Start CRC32 calculate for a block of words
 ******************************************************************************/
uint32_t CRC_CalcCrc32InWord(uint32_t instance,
                             const uint32_t* dataBuff,
                             uint32_t dataLength,
                             uint32_t seed,
                             bool isNewSeed)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);
    ASSERT_PARAM(dataBuff != NULL);

    uint32_t retValue;
    CRC_Type* regBase = s_crcRegBase[instance];

    /* True if the isNewSeed is, then nedd to set the seed */
    if (isNewSeed) {
        CRC_REG_SetSeed(regBase, seed);
    }

    /* Write the data that from the dataBuff to DATA register to calculate checksum */
    for (uint32_t i = 0; i < dataLength; i++) {
        CRC_REG_SetData(regBase, dataBuff[i]);
    }

    /* Get the result by read DATA register */
    retValue = CRC_REG_GetData(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : CRC_CalcCrc16InByte
 *** Description   : Start CRC32 calculate for a block of words
 ******************************************************************************/
uint16_t CRC_CalcCrc16InByte(uint32_t instance,
                             const uint8_t* dataBuff,
                             uint32_t dataLength,
                             uint16_t seed,
                             bool isNewSeed)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);
    ASSERT_PARAM(dataBuff != NULL);

    uint16_t retValue;
    CRC_Type* regBase = s_crcRegBase[instance];

    /* True if the isNewSeed is, then nedd to set the seed */
    if (isNewSeed) {
        CRC_REG_SetSeedL(regBase, seed);
    }

    /* Write the data that from the dataBuff to DATA register to calculate checksum */
    for (uint32_t i = 0; i < dataLength; i++) {
        CRC_REG_SetDataLL(regBase, dataBuff[i]);
    }

    /* Get the result by read DATA register */
    retValue = CRC_REG_GetDataL(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : CRC_CalcCrc16InHalfword
 *** Description   : Start CRC32 calculate for a block of Halfwords
 ******************************************************************************/
uint16_t CRC_CalcCrc16InHalfword(uint32_t instance,
                                 const uint16_t* dataBuff,
                                 uint32_t dataLength,
                                 uint16_t seed,
                                 bool isNewSeed)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);
    ASSERT_PARAM(dataBuff != NULL);

    uint16_t retValue;
    CRC_Type* regBase = s_crcRegBase[instance];

    /* True if the isNewSeed is, then nedd to set the seed */
    if (isNewSeed) {
        CRC_REG_SetSeedL(regBase, seed);
    }

    /* Write the data that from the dataBuff to DATA register to calculate checksum */
    for (uint32_t i = 0; i < dataLength; i++) {
        CRC_REG_SetDataL(regBase, dataBuff[i]);
    }

    /* Get the result by read DATA register */
    retValue = CRC_REG_GetDataL(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : CRC_CalcCrcByDMA
 *** Description   : Calculate crc, the data is transfered by DMA
 ******************************************************************************/
void CRC_CalcCrcByDMA(uint32_t instance,
                      const crc_dma_config_t* dmaConfig,
                      uint32_t seed,
                      bool isNewSeed)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);
    ASSERT_PARAM(dmaConfig != NULL);

    CRC_Type* regBase = s_crcRegBase[instance];
    dma_channel_config_t channelConfig = {0};

    /* True if the isNewSeed is, then nedd to set the seed */
    if (isNewSeed) {
        CRC_REG_SetSeed(regBase, seed);
    }

    /* Set DMA channel configurations for CRC calculation */
    channelConfig.channel = dmaConfig->channel;
    channelConfig.srcAddress = dmaConfig->srcAddress;
    channelConfig.destAddress = (uint32_t)&regBase->DATAu.DATA;
    channelConfig.srcAddressIncrement = true;
    channelConfig.destAddressIncrement = false;
    channelConfig.srcTransferWidth = dmaConfig->width;
    channelConfig.destTransferWidth = dmaConfig->width;
    channelConfig.transferSize = dmaConfig->dataLength;
    channelConfig.transferType = DMA_TRANSFER_MEM2MEM;
    channelConfig.callback = dmaConfig->callback;
    channelConfig.callbackParam = dmaConfig->callbackParam;
    channelConfig.completeIntEnable = true;
    channelConfig.errorIntEnable = true;
    DMA_ChannelInit(&channelConfig);
    /* Start DMA channel to transfer data */
    DMA_StartChannel(channelConfig.channel);
}

/*******************************************************************************
 *** Function Name : CRC_GetCrcResult
 *** Description   : Returns the current result of the CRC calculation
 ******************************************************************************/
uint32_t CRC_GetCrcResult(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_CRC_INSTANCE_COUNT);

    CRC_Type* regBase = s_crcRegBase[instance];

    /* Get the result by read DATA register */
    uint32_t retValue = CRC_REG_GetData(regBase);

    return retValue;
}

/*******************************************************************************
 *** Function Name : CRC_GetDefaultConfig
 *** Description   : Get default configures the CRC module for configuration structure
 ******************************************************************************/
void CRC_GetDefaultConfig(crc_config_t* crcConfig)
{
    ASSERT_PARAM(crcConfig != NULL);

    crcConfig->seed = 0x0000FFFFU;
    crcConfig->polynomial  = 0x1021U;
    crcConfig->checksumXOR = false;
    crcConfig->crcProtocol = CRC16_PROTOCOL;
    crcConfig->writeTranspose = CRC_TRANSPOSE_NONE;
    crcConfig->readTranspose  = CRC_TRANSPOSE_NONE;
}

/*******EOF********************************************************************/
